﻿using huqiang.Core.UIData;
using huqiang.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace huqiang.Core.HGUI
{
    public class UIAdaptive
    {
        #region static method
        static UIElement[] buff = new UIElement[64];
        public static Coordinates GetGlobaInfo(UIElement trans, bool Includeroot = true)
        {
            buff[0] = trans;
            var parent = trans.parent;
            int max = 1;
            if (parent != null)
            {
                for (; max < 64; max++)
                {
                    buff[max] = parent;
                    parent = parent.parent;
                    if (parent == null)
                        break;
                }
            }
            else max = 0;

            Vector3 pos, scale;
            Quaternion quate;
            if (Includeroot)
            {
                var p = buff[max];
                pos = p.localPosition;
                scale = p.localScale;
                quate = p.localRotation;
                max--;
            }
            else
            {
                pos = Vector3.zero;
                scale = Vector3.one;
                quate = Quaternion.identity;
                max--;
            }
            for (; max >= 0; max--)
            {
                var rt = buff[max];
                Vector3 p = rt.localPosition;
                Vector3 o = Vector3.zero;
                o.x = p.x * scale.x;
                o.y = p.y * scale.y;
                o.z = p.z * scale.z;
                pos += quate * o;
                quate *= rt.localRotation;
                Vector3 s = rt.localScale;
                scale.x *= s.x;
                scale.y *= s.y;
            }
            Coordinates coord = new Coordinates();
            coord.Position = pos;
            coord.Rotation = quate;
            coord.Scale = scale;
            return coord;
        }
        public static Vector3 ScreenToLocal(UIElement trans, Vector3 v)
        {
            var g = GetGlobaInfo(trans, false);
            v -= g.Position;
            if (g.Scale.x != 0)
                v.x /= g.Scale.x;
            else v.x = 0;
            if (g.Scale.y != 0)
                v.y /= g.Scale.y;
            else v.y = 0;
            if (g.Scale.z != 0)
                v.z /= g.Scale.z;
            else v.z = 0;
            var q = Quaternion.Inverse(g.Rotation);
            v = q * v;
            return v;
        }
        public static Vector2[] Anchors = new[] { new Vector2(0.5f, 0.5f), new Vector2(0, 0.5f),new Vector2(1, 0.5f),
        new Vector2(0.5f, 1),new Vector2(0.5f, 0), new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1)};
        public static void Scaling(UIElement rect, ScaleType type, Vector2 pSize, Vector2 ds)
        {
            switch (type)
            {
                case ScaleType.None:
                    break;
                case ScaleType.FillX:
                    float sx = pSize.x / ds.x;
                    rect.localScale = new Vector3(sx, sx, sx);
                    break;
                case ScaleType.FillY:
                    float sy = pSize.y / ds.y;
                    rect.localScale = new Vector3(sy, sy, sy);
                    break;
                case ScaleType.FillXY:
                    sx = pSize.x / ds.x;
                    sy = pSize.y / ds.y;
                    if (sx < sy)
                        rect.localScale = new Vector3(sx, sx, sx);
                    else rect.localScale = new Vector3(sy, sy, sy);
                    break;
                case ScaleType.Cover:
                    sx = pSize.x / ds.x;
                    sy = pSize.y / ds.y;
                    if (sx < sy)
                        rect.localScale = new Vector3(sy, sy, sy);
                    else rect.localScale = new Vector3(sx, sx, sx);
                    break;
            }
        }
        public static void AnchorEx(UIElement script, AnchorPointType type, Vector2 offset, Vector2 p, Vector2 psize)
        {
            Vector2 pivot = Anchors[(int)type];
            float x = psize.x;
            float y = psize.y;
            float px = p.x;
            float py = p.y;

            float lx = x * -px;
            float dy = y * -py;

            float tx = lx + pivot.x * psize.x;//锚点x
            float ty = dy + pivot.y * psize.y;//锚点y
            offset.x += tx;//偏移点x
            offset.y += ty;//偏移点y
            script.localPosition = new Vector3(offset.x, offset.y, 0);
        }
        public static void AlignmentEx(UIElement script, AnchorPointType type, Vector2 offset, Vector2 p, Vector2 psize)
        {
            //Vector2 pivot = Anchors[(int)type];
            float ax = psize.x;
            float ay = psize.y;
            float apx = p.x;
            float apy = p.y;
            float alx = ax * -apx;
            float ady = ay * -apy;
            //float aox = ax * apx;//原点x
            //float aoy = ay * apy;//原点y

            float x = script.SizeDelta.x;
            float y = script.SizeDelta.y;
            float px = script.Pivot.x;
            float py = script.Pivot.y;
            float lx = x * -px;
            float dy = y * -py;

            //float ox = x * px;//原点x
            //float oy = y * py;//原点y

            switch (type)
            {
                case AnchorPointType.Left:
                    x = alx - lx;
                    y = (ady + ay * 0.5f) - (dy + y * 0.5f);
                    break;
                case AnchorPointType.Right:
                    x = (ax + alx) - (x + lx);
                    y = (ady + ay * 0.5f) - (dy + y * 0.5f);
                    break;
                case AnchorPointType.Top:
                    x = (alx + ax * 0.5f) - (lx + x * 0.5f);
                    y = (ay + ady) - (y + dy);
                    break;
                case AnchorPointType.Down:
                    x = (alx + ax * 0.5f) - (lx + x * 0.5f);
                    y = ady - dy;
                    break;
                case AnchorPointType.LeftDown:
                    x = alx - lx;
                    y = ady - dy;
                    break;
                case AnchorPointType.LeftTop:
                    x = alx - lx;
                    y = (ay + ady) - (y + dy);
                    break;
                case AnchorPointType.RightDown:
                    x = (ax + alx) - (x + lx);
                    y = ady - dy;
                    break;
                case AnchorPointType.RightTop:
                    x = (ax + alx) - (x + lx);
                    y = (ay + ady) - (y + dy);
                    break;
                default:
                    x = (alx + ax * 0.5f) - (lx + x * 0.5f);
                    y = (ady + ay * 0.5f) - (dy + y * 0.5f);
                    break;
            }
            x += offset.x;
            y += offset.y;
            script.localPosition = new Vector3(x, y, 0);
        }
        public static void MarginEx(UIElement script, Margin margin, Vector2 parentPivot, Vector2 parentSize)
        {
            float w = parentSize.x - margin.left - margin.right;
            float h = parentSize.y - margin.top - margin.down;
            var m_pivot = script.Pivot;
            float ox = w * m_pivot.x - parentPivot.x * parentSize.x + margin.left;
            float oy = h * m_pivot.y - parentPivot.y * parentSize.y + margin.down;
            float sx = script.localScale.x;
            float sy = script.localScale.y;
            script.SizeDelta = new Vector2(w / sx, h / sy);
            script.localPosition = new Vector3(ox, oy, 0);
        }
        public static void MarginX(UIElement script, Margin margin, Vector2 parentPivot, Vector2 parentSize)
        {
            float w = parentSize.x - margin.left - margin.right;
            var m_pivot = script.Pivot;
            float ox = w * m_pivot.x - parentPivot.x * parentSize.x + margin.left;
            float sx = script.localScale.x;
            float y = script.SizeDelta.y;
            script.SizeDelta = new Vector2(w / sx, y);
            float py = script.localPosition.y;
            script.localPosition = new Vector3(ox, py, 0);
        }
        public static void MarginY(UIElement script, Margin margin, Vector2 parentPivot, Vector2 parentSize)
        {
            float h = parentSize.y - margin.top - margin.down;
            var m_pivot = script.Pivot;
            float oy = h * m_pivot.y - parentPivot.y * parentSize.y + margin.down;
            float sy = script.localScale.y;
            float x = script.SizeDelta.x;
            script.SizeDelta = new Vector2(x, h / sy);
            float px = script.localPosition.x;
            script.localPosition = new Vector3(px, oy, 0);
        }
        public static void Resize(UIElement script, bool child = true)
        {
            Vector2 psize = Vector2.zero;
            Vector2 v = script.m_sizeDelta;
            var pp = Anchors[0];
            switch (script.parentType)
            {
                case ParentType.Tranfrom:
                    var p = script._parent;
                    if (p != null)
                    {
                        psize = p.SizeDelta;
                        pp = p.Pivot;
                    }
                    break;
                case ParentType.Screen:
                    var t = script.root;
                    if (t != null)
                        psize = t.SizeDelta;
                    break;
                case ParentType.SafeArea:
                    psize = UISystem.safeArea.size;
                    t = script.root;
                    if (t != null)
                    {
                        psize.x /= t.localScale.x;
                        psize.y /= t.localScale.y;
                    }
                    break;
                case ParentType.SafeAreaXAndScreenY:
                    psize = UISystem.safeArea.size;
                    t = script.root;
                    if (t != null)
                    {
                        psize.x /= t.localScale.x;
                        psize.y = t.m_sizeDelta.y;
                    }
                    break;
                case ParentType.SafeAreaYAndScreenX:
                    psize = UISystem.safeArea.size;
                    t = script.root;
                    if (t != null)
                    {
                        psize.x = t.m_sizeDelta.x;
                        psize.y /= t.localScale.y;
                    }
                    break;
            }
            switch (script.marginType)
            {
                case MarginType.None:
                    break;
                case MarginType.Margin:
                    MarginEx(script, script.margin, pp, psize);
                    break;
                case MarginType.MarginRatio:
                    var mar = new Margin();
                    mar.left = script.margin.left * psize.x;
                    mar.right = script.margin.right * psize.x;
                    mar.top = script.margin.top * psize.y;
                    mar.down = script.margin.down * psize.y;
                    MarginEx(script, mar, pp, psize);
                    break;
                case MarginType.MarginX:
                    MarginX(script, script.margin, pp, psize);
                    break;
                case MarginType.MarginY:
                    MarginY(script, script.margin, pp, psize);
                    break;
                case MarginType.MarginRatioX:
                    mar = new Margin();
                    mar.left = script.margin.left * psize.x;
                    mar.right = script.margin.right * psize.x;
                    MarginX(script, mar, pp, psize);
                    break;
                case MarginType.MarginRatioY:
                    mar = new Margin();
                    mar.top = script.margin.top * psize.y;
                    mar.down = script.margin.down * psize.y;
                    MarginY(script, mar, pp, psize);
                    break;
                case MarginType.Size:
                    script.m_sizeDelta.x = psize.x - script.margin.left;
                    script.m_sizeDelta.y = psize.y - script.margin.down;
                    break;
                case MarginType.Ratio:
                    script.m_sizeDelta.x = psize.x * (1 - script.margin.left);
                    script.m_sizeDelta.y = psize.y * (1 - script.margin.down);
                    break;
                case MarginType.SizeX:
                    script.m_sizeDelta.x = psize.x - script.margin.left;
                    break;
                case MarginType.SizeY:
                    script.m_sizeDelta.y = psize.y - script.margin.down;
                    break;
                case MarginType.RatioX:
                    script.m_sizeDelta.x = psize.x * (1 - script.margin.left);
                    break;
                case MarginType.RatioY:
                    script.m_sizeDelta.y = psize.y * (1 - script.margin.down);
                    break;
            }
            if (script.parentType >= ParentType.SafeArea)
            {
                Coordinates coor = new Coordinates();
                Vector2 c = UISystem.safeArea.center;
                var t = script.root;
                if (t != null)
                {
                    c.x /= t.localScale.x;
                    c.y /= t.localScale.y;
                    c.x -= t.m_sizeDelta.x * 0.5f;
                    c.y -= t.m_sizeDelta.y * 0.5f;
                }
                coor.Position = c;
                coor.Scale = Vector3.one;
                coor.Rotation = Quaternion.identity;
                switch (script.parentType)
                {
                    case ParentType.SafeAreaXAndScreenY:
                        coor.Position.y = 0;
                        break;
                    case ParentType.SafeAreaYAndScreenX:
                        coor.Position.x = 0;
                        break;
                }
                script.coordinate = coor;
            }
            switch (script.anchorType)
            {
                case AnchorType.None:
                    break;
                case AnchorType.Anchor:
                    AnchorEx(script, script.anchorPointType, script.anchorOffset, pp, psize);
                    break;
                case AnchorType.Alignment:
                    AlignmentEx(script, script.anchorPointType, script.anchorOffset, pp, psize);
                    break;
            }
            if (script.scaleType != ScaleType.None)
                Scaling(script, script.scaleType, psize, script.m_sizeDelta);
            if (child)
                ResizeChild(script, child);
            if (script.scaleType != ScaleType.None | script.anchorType != AnchorType.None | script.marginType != MarginType.None)
            {
                ResizeChild(script, false);
                if (v != script.m_sizeDelta)
                    script.ReSized();
            }
        }
        public static void ResizeChild(UIElement trans, bool child = true)
        {
            if (trans == null)
                return;
            var chi = trans.child;
            for (int i = 0; i < chi.Count; i++)
            {
                var son = chi[i];
                if (son != null)
                    Resize(son, child);
                else if (child)
                    ResizeChild(son, child);
            }
        }
        public static void Margin(UIElement script)
        {
            //Vector3 loclpos = script.localPosition;
            Vector2 psize = Vector2.zero;
            //Vector2 v = script.m_sizeDelta;
            var pp = Anchors[0];
            if (script.parentType == ParentType.Tranfrom)
            {
                var p = script._parent;
                if (p != null)
                {
                    psize = p.SizeDelta;
                    pp = p.Pivot;
                }
            }
            else
            {
                var t = script.root;
                if (t != null)
                    psize = t.SizeDelta;
            }
            switch (script.marginType)
            {
                case MarginType.None:
                    break;
                case MarginType.Margin:
                    var mar = script.margin;
                    MarginEx(script, mar, pp, psize);
                    break;
                case MarginType.MarginRatio:
                    mar = new Margin();
                    mar.left = script.margin.left * psize.x;
                    mar.right = script.margin.right * psize.x;
                    mar.top = script.margin.top * psize.y;
                    mar.down = script.margin.down * psize.y;
                    MarginEx(script, mar, pp, psize);
                    break;
                case MarginType.MarginX:
                    mar = script.margin;
                    MarginX(script, mar, pp, psize);
                    break;
                case MarginType.MarginY:
                    mar = script.margin;
                    MarginY(script, mar, pp, psize);
                    break;
                case MarginType.MarginRatioX:
                    mar = new Margin();
                    mar.left = script.margin.left * psize.x;
                    mar.right = script.margin.right * psize.x;
                    MarginX(script, mar, pp, psize);
                    break;
                case MarginType.MarginRatioY:
                    mar = new Margin();
                    mar.top = script.margin.top * psize.y;
                    mar.down = script.margin.down * psize.y;
                    MarginY(script, mar, pp, psize);
                    break;
                case MarginType.Size:
                    script.m_sizeDelta.x = psize.x - script.margin.left;
                    script.m_sizeDelta.y = psize.y - script.margin.down;
                    break;
                case MarginType.Ratio:
                    script.m_sizeDelta.x = psize.x * (1 - script.margin.left);
                    script.m_sizeDelta.y = psize.y * (1 - script.margin.down);
                    break;
                case MarginType.SizeX:
                    script.m_sizeDelta.x = psize.x - script.margin.left;
                    break;
                case MarginType.SizeY:
                    script.m_sizeDelta.y = psize.y - script.margin.down;
                    break;
                case MarginType.RatioX:
                    script.m_sizeDelta.x = psize.x * (1 - script.margin.left);
                    break;
                case MarginType.RatioY:
                    script.m_sizeDelta.y = psize.y * (1 - script.margin.down);
                    break;
            }
        }
        public static void Dock(UIElement script)
        {
            Vector3 loclpos = script.localPosition;
            Vector2 psize = Vector2.zero;
            Vector2 v = script.m_sizeDelta;
            var pp = Anchors[0];
            if (script.parentType == ParentType.Tranfrom)
            {
                var p = script._parent;
                if (p != null)
                {
                    psize = p.SizeDelta;
                    pp = p.Pivot;
                }
            }
            else
            {
                var t = script.root;
                if (t != null)
                    psize = t.SizeDelta;
            }
            switch (script.anchorType)
            {
                case AnchorType.None:
                    break;
                case AnchorType.Anchor:
                    AnchorEx(script, script.anchorPointType, script.anchorOffset, pp, psize);
                    break;
                case AnchorType.Alignment:
                    AlignmentEx(script, script.anchorPointType, script.anchorOffset, pp, psize);
                    break;
            }
        }
        public static Vector2 GetSize(UIElement parent, FakeStruct ele)
        {
            unsafe
            {
                Vector2 psize = Vector2.zero;
                UIElementData* up = (UIElementData*)ele.ip;
                switch (up->parentType)
                {
                    case ParentType.Screen:
                        psize = HCanvas.CurrentCanvas.m_sizeDelta;
                        break;
                    case ParentType.Tranfrom:
                        if (parent != null)
                            psize = parent.m_sizeDelta;
                        break;
                    case ParentType.SafeArea:
                        psize = UISystem.safeArea.size;
                        float s = HCanvas.CurrentCanvas.localScale.x;
                        psize /= s;
                        break;
                }
                switch (up->marginType)
                {
                    case MarginType.None:
                        return up->m_sizeDelta;
                    case MarginType.Margin:
                        psize.x -= up->margin.left + up->margin.right;
                        psize.y -= up->margin.top + up->margin.down;
                        return psize;
                    case MarginType.MarginRatio:
                        psize.x *= (1 - up->margin.left - up->margin.right);
                        psize.y *= (1 - up->margin.top - up->margin.down);
                        return psize;
                    case MarginType.MarginX:
                        psize.x -= up->margin.left + up->margin.right;
                        psize.y = up->m_sizeDelta.y;
                        return psize;
                    case MarginType.MarginY:
                        psize.x = up->m_sizeDelta.x;
                        psize.y -= up->margin.top + up->margin.down;
                        return psize;
                    case MarginType.MarginRatioX:
                        psize.x *= (1 - up->margin.left - up->margin.right);
                        psize.y = up->m_sizeDelta.y;
                        return psize;
                    case MarginType.MarginRatioY:
                        psize.x = up->m_sizeDelta.x;
                        psize.y *= (1 - up->margin.top - up->margin.down);
                        return psize;
                    case MarginType.Size:
                        psize.x -= up->margin.left;
                        psize.y -= up->margin.down;
                        return psize;
                    case MarginType.Ratio:
                        psize.x *= (1 - up->margin.left);
                        psize.y *= (1 - up->margin.down);
                        return psize;
                    case MarginType.SizeX:
                        psize.x -= up->margin.left;
                        psize.y = up->m_sizeDelta.y;
                        return psize;
                    case MarginType.SizeY:
                        psize.x = up->m_sizeDelta.x;
                        psize.y -= up->margin.down;
                        return psize;
                    case MarginType.RatioX:
                        psize.x *= (1 - up->margin.left);
                        psize.y = up->m_sizeDelta.y;
                        return psize;
                    case MarginType.RatioY:
                        psize.x = up->m_sizeDelta.x;
                        psize.y *= (1 - up->margin.down);
                        return psize;
                }
                return up->m_sizeDelta;
            }
        }
        #endregion
    }
}
